Add APIs to get xattrs from disk
authorColin Walters <walters@verbum.org>
Sat, 7 May 2022 17:51:31 +0000 (13:51 -0400)
committerColin Walters <walters@verbum.org>
Mon, 9 May 2022 16:33:38 +0000 (12:33 -0400)
I'm aiming to do some more work on the Rust side around `fsck`
like functionality, and this is a useful primitive.  There isn't
a great Rust crate for xattrs, and I think it's better to share this
code.

Makefile-libostree.am
apidoc/ostree-sections.txt
src/libostree/libostree-devel.sym
src/libostree/ostree-core.c
src/libostree/ostree-core.h
src/libostree/ostree-repo-commit.c
src/libostree/ostree-repo.c
tests/test-basic-c.c

index b58106aa717f06bab9bd2506d6c61f8190b27add..af89ce9a4a5379e7525076a53e16bcb2331088f3 100644 (file)
@@ -171,9 +171,9 @@ endif # USE_GPGME
 symbol_files = $(top_srcdir)/src/libostree/libostree-released.sym
 
 # Uncomment this include when adding new development symbols.
-#if BUILDOPT_IS_DEVEL_BUILD
-#symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
-#endif
+if BUILDOPT_IS_DEVEL_BUILD
+symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
+endif
 
 # http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html
 wl_versionscript_arg = -Wl,--version-script=
index 577ee8080f258584b08450cdf7912149445da8e8..adf52557bbe2377ba2302021cde16b57e8d5d5b3 100644 (file)
@@ -143,6 +143,8 @@ ostree_checksum_file
 ostree_checksum_file_at
 ostree_checksum_file_async
 ostree_checksum_file_async_finish
+ostree_fs_get_all_xattrs
+ostree_fs_get_all_xattrs_at
 ostree_create_directory_metadata
 ostree_validate_structureof_objtype
 ostree_validate_structureof_csum_v
index c15ae9fa9586c7ed9837754310458940468092e5..13e8041bddb24e3dacd9abbb0af56aaa2703a055 100644 (file)
    - uncomment the include in Makefile-libostree.am
 */
 
-
+LIBOSTREE_2022.4 {
+global:
+  ostree_fs_get_all_xattrs;
+  ostree_fs_get_all_xattrs_at;
+} LIBOSTREE_2021.5;
 
 /* Stub section for the stable release *after* this development one; don't
  * edit this other than to update the year.  This is just a copy/paste
index 794f0e1150e727d7861b7008f015fdeff127fa75..56b381d91827092664d047d10e3cd38cfcaa3163 100644 (file)
@@ -836,6 +836,51 @@ gboolean ostree_break_hardlink (int               dfd,
   return TRUE;
 }
 
+/**
+ * ostree_fs_get_all_xattrs:
+ * @fd: File descriptor
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Retrieve all extended attributes in a canonical (sorted) order from
+ * the given file descriptor.
+ *
+ * Returns: (transfer full): A GVariant of type `a(ayay)`
+ */
+GVariant *
+ostree_fs_get_all_xattrs (int fd, GCancellable *cancellable, GError **error)
+{
+  GVariant *ret = NULL;
+  if (!glnx_fd_get_all_xattrs (fd, &ret, cancellable, error))
+    return NULL;
+  return ret;
+}
+
+/**
+ * ostree_fs_get_all_xattrs_at:
+ * @dfd: Directory file descriptor
+ * @path: Filesystem path
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Retrieve all extended attributes in a canonical (sorted) order from
+ * the given path, relative to the provided directory file descriptor.
+ * The target path will not be dereferenced.  Currently on Linux, this
+ * API must be used currently to retrieve extended attributes
+ * for symbolic links because while `O_PATH` exists, it cannot be used
+ * with `fgetxattr()`.
+ *
+ * Returns: (transfer full): A GVariant of type `a(ayay)`
+ */
+GVariant *
+ostree_fs_get_all_xattrs_at (int dfd, const char *path, GCancellable *cancellable, GError **error)
+{
+  GVariant *ret = NULL;
+  if (!glnx_dfd_name_get_all_xattrs (dfd, path, &ret, cancellable, error))
+    return NULL;
+  return ret;
+}
+
 /**
  * ostree_checksum_file_from_input:
  * @file_info: File information
@@ -928,8 +973,8 @@ ostree_checksum_file (GFile            *f,
   g_autoptr(GVariant) xattrs = NULL;
   if (objtype == OSTREE_OBJECT_TYPE_FILE)
     {
-      if (!glnx_dfd_name_get_all_xattrs (AT_FDCWD, gs_file_get_path_cached (f),
-                                         &xattrs, cancellable, error))
+      xattrs = ostree_fs_get_all_xattrs_at (AT_FDCWD, gs_file_get_path_cached (f), cancellable, error);
+      if (!xattrs)
         return FALSE;
     }
 
index 9a14192d74ee6178d49377a47a04d857d240f104..0d4dca8e7ecbccd0bdca64a7089c455a98b0480f 100644 (file)
@@ -464,6 +464,12 @@ gboolean ostree_break_hardlink (int               dfd,
                                 GCancellable     *cancellable,
                                 GError          **error);
 
+_OSTREE_PUBLIC
+GVariant *ostree_fs_get_all_xattrs (int fd, GCancellable *cancellable, GError **error);
+
+_OSTREE_PUBLIC
+GVariant *ostree_fs_get_all_xattrs_at (int dfd, const char *path, GCancellable *cancellable, GError **error);
+
 /**
  * OstreeChecksumFlags:
  * @OSTREE_CHECKSUM_FLAGS_NONE: Default checksumming without tweaks.
index 0af8fee3bd4131c643324a7979d8c8ab6a1f3119..afab3fdf85c6c0abee235f222b3b037aad3a72b8 100644 (file)
@@ -3436,14 +3436,15 @@ get_final_xattrs (OstreeRepo                       *self,
       else if (dfd_subpath == NULL)
         {
           g_assert (dfd != -1);
-          if (!glnx_fd_get_all_xattrs (dfd, &original_xattrs, cancellable, error))
+          original_xattrs = ostree_fs_get_all_xattrs (dfd, cancellable, error);
+          if (!original_xattrs)
             return FALSE;
         }
       else
         {
           g_assert (dfd != -1);
-          if (!glnx_dfd_name_get_all_xattrs (dfd, dfd_subpath, &original_xattrs,
-                                             cancellable, error))
+          original_xattrs = ostree_fs_get_all_xattrs_at (dfd, dfd_subpath, cancellable, error);
+          if (!original_xattrs)
             return FALSE;
         }
 
@@ -4641,8 +4642,8 @@ import_one_object_direct (OstreeRepo    *dest_repo,
           if (src_repo->mode == OSTREE_REPO_MODE_BARE)
             {
               g_autoptr(GVariant) xattrs = NULL;
-              if (!glnx_fd_get_all_xattrs (src_fd, &xattrs,
-                                           cancellable, error))
+              xattrs = ostree_fs_get_all_xattrs (src_fd, cancellable, error);
+              if (!xattrs)
                 return FALSE;
               if (!glnx_fd_set_all_xattrs (tmp_dest.fd, xattrs,
                                            cancellable, error))
index ad071c1703e039ea1b80266a17c265165a79b748..994db626d6f30576e653efe3aa63f10278c52688 100644 (file)
@@ -28,6 +28,7 @@
 #include <gio/gunixoutputstream.h>
 #include <gio/gfiledescriptorbased.h>
 #include "libglnx.h"
+#include "ostree-core.h"
 #include "otutil.h"
 #include <glnx-console.h>
 #include <linux/magic.h>
@@ -4345,9 +4346,12 @@ _ostree_repo_load_file_bare (OstreeRepo         *self,
         {
           if (self->disable_xattrs)
             ret_xattrs = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("(ayay)"), NULL, 0));
-          else if (!glnx_fd_get_all_xattrs (fd, &ret_xattrs,
-                                            cancellable, error))
-            return FALSE;
+          else 
+            {
+              ret_xattrs = ostree_fs_get_all_xattrs (fd, cancellable, error);
+              if (!ret_xattrs)
+                return FALSE;
+            }
         }
       else if (S_ISLNK (stbuf.st_mode) && out_xattrs)
         {
index bac7d56d053eb814485200f002fdcb3cda64eabb..1886feb2ddaefedd72175603aa01811b022ae29b 100644 (file)
@@ -20,6 +20,7 @@
 #include "config.h"
 
 #include <stdlib.h>
+#include <stdbool.h>
 #include <gio/gio.h>
 #include <string.h>
 #include <err.h>
@@ -476,6 +477,76 @@ test_big_metadata (void)
   g_assert (ret);
 }
 
+static void
+compare_xattrs (GVariant *orig, GVariant *new)
+{
+  g_assert_cmpint (g_variant_n_children (orig) + 1, ==, g_variant_n_children (new));
+  GVariant *kp, *vp;
+  GVariantIter iter;
+  g_variant_iter_init (&iter, new);
+  bool found = false;
+  while (g_variant_iter_loop (&iter, "(@ay@ay)", &kp, &vp))
+    {
+      const char *k = g_variant_get_bytestring (kp);
+      if (!g_str_equal (k, "user.ostreetesting"))
+        continue;
+      g_assert_cmpint (g_variant_get_size (vp), ==, 4);
+      found = true;
+    }
+  g_assert (found);
+}
+
+static void
+test_read_xattrs (void)
+{
+  g_autoptr(GError) local_error = NULL;
+  GError **error = &local_error;
+
+  g_auto(GLnxTmpDir) tmpd = { 0, };
+  // Use /var/tmp to hope we get xattr support
+  glnx_mkdtempat (AT_FDCWD, "/var/tmp/ostree-xattrs-test.XXXXXX", 0700, &tmpd, error);
+  g_assert_no_error (local_error);
+
+  const char value[] = "foo";
+
+  {
+    g_autoptr(GVariant) current_xattrs = ostree_fs_get_all_xattrs (tmpd.fd, NULL, error);
+    g_assert_no_error (local_error);
+  
+    int r = fsetxattr (tmpd.fd, "user.ostreetesting", value, sizeof (value), 0);
+    g_assert_cmpint (r, ==, 0);
+  
+    g_autoptr(GVariant) new_xattrs = ostree_fs_get_all_xattrs (tmpd.fd, NULL, error);
+    g_assert_no_error (local_error);
+  
+    compare_xattrs (current_xattrs, new_xattrs);
+  }
+
+  {
+    if (symlinkat ("nosuchtarget", tmpd.fd, "somelink") < 0)
+      glnx_throw_errno_prefix (error, "symlinkat");
+    g_assert_no_error (local_error);
+
+    g_autoptr(GVariant) current_xattrs = ostree_fs_get_all_xattrs_at (tmpd.fd, "somelink", NULL, error);
+    g_assert_no_error (local_error);
+    (void) current_xattrs;
+
+    // OK, can't do user. xattrs on symlinks unfortunately.
+
+    // char pathbuf[PATH_MAX];
+    // snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", tmpd.fd, "somelink");
+    // int r = lsetxattr (pathbuf, "user.ostreetesting", value, sizeof (value), 0);
+    // if (r < 0)
+    //   glnx_throw_errno_prefix (error, "lsetxattr");
+    // g_assert_no_error (local_error);
+  
+    // g_autoptr(GVariant) new_xattrs = ostree_fs_get_all_xattrs_at (tmpd.fd, "somelink", NULL, error);
+    // g_assert_no_error (local_error);
+  
+    // compare_xattrs (current_xattrs, new_xattrs);
+  }
+}
+
 int main (int argc, char **argv)
 {
   g_autoptr(GError) error = NULL;
@@ -494,6 +565,7 @@ int main (int argc, char **argv)
   g_test_add_func ("/break-hardlink", test_break_hardlink);
   g_test_add_func ("/remotename", test_validate_remotename);
   g_test_add_func ("/big-metadata", test_big_metadata);
+  g_test_add_func ("/read-xattrs", test_read_xattrs);
 
   return g_test_run();
  out: